
package provider.WzXML;

import java.awt.Point;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import provider.DbgData;
import provider.DbgDataEntity;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XMLDomMapleData implements DbgData, Serializable {

    private Node node;
    private File imageDataDir;

    private XMLDomMapleData(final Node node) {
	this.node = node;
    }

    public XMLDomMapleData(final FileInputStream fis, final File imageDataDir) {
	try {
	    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
	    DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
	    Document document = documentBuilder.parse(fis);
	    this.node = document.getFirstChild();

	} catch (ParserConfigurationException e) {
	    throw new RuntimeException(e);
	} catch (SAXException e) {
	    throw new RuntimeException(e);
	} catch (IOException e) {
	    throw new RuntimeException(e);
	}
	this.imageDataDir = imageDataDir;
    }

    @Override
    public DbgData getChildByPath(final String path) {
	final String segments[] = path.split("/");
	if (segments[0].equals("..")) {
	    return ((DbgData) getParent()).getChildByPath(path.substring(path.indexOf("/") + 1));
	}

	Node myNode = node;
	for (int x = 0; x < segments.length; x++) {
	    NodeList childNodes = myNode.getChildNodes();
	    boolean foundChild = false;
	    for (int i = 0; i < childNodes.getLength(); i++) {
		final Node childNode = childNodes.item(i);
		if (childNode.getNodeType() == Node.ELEMENT_NODE && childNode.getAttributes().getNamedItem("name").getNodeValue().equals(segments[x])) {
		    myNode = childNode;
		    foundChild = true;
		    break;
		}
	    }
	    if (!foundChild) {
		return null;
	    }
	}
	final XMLDomMapleData ret = new XMLDomMapleData(myNode);
	ret.imageDataDir = new File(imageDataDir, getName() + "/" + path).getParentFile();
	return ret;
    }

    @Override
    public List<DbgData> getChildren() {
	final List<DbgData> ret = new ArrayList<DbgData>();
	final NodeList childNodes = node.getChildNodes();
	for (int i = 0; i < childNodes.getLength(); i++) {
	    final Node childNode = childNodes.item(i);
	    if (childNode.getNodeType() == Node.ELEMENT_NODE) {
		final XMLDomMapleData child = new XMLDomMapleData(childNode);
		child.imageDataDir = new File(imageDataDir, getName());
		ret.add(child);
	    }
	}
	return ret;
    }

    @Override
    public Object getData() {
	final NamedNodeMap attributes = node.getAttributes();
	final DbgDataType type = getType();
	switch (type) {
	    case DOUBLE: {
		return Double.valueOf(Double.parseDouble(attributes.getNamedItem("value").getNodeValue()));
	    }
	    case FLOAT: {
		return Float.valueOf(Float.parseFloat(attributes.getNamedItem("value").getNodeValue()));
	    }
	    case INT: {
		return Integer.valueOf(Integer.parseInt(attributes.getNamedItem("value").getNodeValue()));
	    }
	    case SHORT: {
		return Short.valueOf(Short.parseShort(attributes.getNamedItem("value").getNodeValue()));
	    }
	    case STRING:
	    case UOL: {
		return attributes.getNamedItem("value").getNodeValue();
	    }
	    case VECTOR: {
		return new Point(Integer.parseInt(attributes.getNamedItem("x").getNodeValue()), Integer.parseInt(attributes.getNamedItem("y").getNodeValue()));
	    }
	    case CANVAS: {
		return new FileStoredPngMapleCanvas(Integer.parseInt(attributes.getNamedItem("width").getNodeValue()), Integer.parseInt(attributes.getNamedItem("height").getNodeValue()), new File(imageDataDir, getName() + ".png"));
	    }
	}
	return null;
    }

    public final DbgDataType getType() {
	final String nodeName = node.getNodeName();
	if (nodeName.equals("imgdir")) {
	    return DbgDataType.PROPERTY;
	} else if (nodeName.equals("canvas")) {
	    return DbgDataType.CANVAS;
	} else if (nodeName.equals("convex")) {
	    return DbgDataType.CONVEX;
	} else if (nodeName.equals("sound")) {
	    return DbgDataType.SOUND;
	} else if (nodeName.equals("uol")) {
	    return DbgDataType.UOL;
	} else if (nodeName.equals("double")) {
	    return DbgDataType.DOUBLE;
	} else if (nodeName.equals("float")) {
	    return DbgDataType.FLOAT;
	} else if (nodeName.equals("int")) {
	    return DbgDataType.INT;
	} else if (nodeName.equals("short")) {
	    return DbgDataType.SHORT;
	} else if (nodeName.equals("string")) {
	    return DbgDataType.STRING;
	} else if (nodeName.equals("vector")) {
	    return DbgDataType.VECTOR;
	} else if (nodeName.equals("null")) {
	    return DbgDataType.IMG_0x00;
	}
	return null;
    }

    @Override
    public DbgDataEntity getParent() {
	final Node parentNode = node.getParentNode();
	if (parentNode.getNodeType() == Node.DOCUMENT_NODE) {
	    return null; // can't traverse outside the img file - TODO is this a problem?
	}
	final XMLDomMapleData parentData = new XMLDomMapleData(parentNode);
	parentData.imageDataDir = imageDataDir.getParentFile();
	return parentData;
    }

    @Override
    public String getName() {
	return node.getAttributes().getNamedItem("name").getNodeValue();
    }

    @Override
    public Iterator<DbgData> iterator() {
	return getChildren().iterator();
    }
}
